﻿using Cinch;
using FR.SAFTAnalyserLite.Model;
using FR.SAFTAnalyserLite.ViewModels.Pane;
using MEFedMVVM.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace FR.SAFTAnalyserLite.ViewModels.Tools
{
    public class CreateHashFromUserInputViewModel : ToolViewModel, ITool
    {
        public CreateHashFromUserInputViewModel()
            : base("Criar Hash")
        {
            if (!Designer.IsInDesignMode)
            {
                //Commands
                DoCreateHashCommand = new SimpleCommand<object, object>(CanCreateHash, CreateHash);
                DoTestHashCommand = new SimpleCommand<object, object>(CanTestHash, TestHash);
                DoCleanFieldsCommand = new SimpleCommand<object, object>(CanCleanFields, CleanFields);
            }
        }

        public void InitFromInvoice(SourceDocumentsSalesInvoicesInvoice invoice, string hashDocumentoAnterior)
        {
            if (invoice != null)
            {
                this.TiposDocumentos = Enum.GetNames(typeof(Model.InvoiceType));
                this.TipoDocumento = invoice.InvoiceType.ToString();
                this.TotalDocumento = invoice.DocumentTotals.GrossTotal;
                this.DataDocumento = invoice.InvoiceDate;
                this.DataSistema = invoice.SystemEntryDate;
                this.HashAnterior = hashDocumentoAnterior;
                this.HashActualDocumento = invoice.Hash;
                string[] invoiceNo = invoice.InvoiceNo.Split('/');
                if (invoiceNo != null && invoiceNo.Length == 2)
                {
                    int num = 0;
                    Int32.TryParse(invoiceNo[1], out num);
                    this.NumeroDocumento = num;

                    string[] tipoSerie = invoiceNo[0].Split(' ');
                    if (tipoSerie != null && tipoSerie.Length == 2)
                    {
                        this.SerieDocumento = tipoSerie[1];
                    }
                }

                this.ValoresLidosDocumento = true;
            }
        }

        public void InitFromMovement(SourceDocumentsMovementOfGoodsStockMovement movement, string hashDocumentoAnterior)
        {
            if (movement != null)
            {
                this.TiposDocumentos = Enum.GetNames(typeof(Model.MovementType));
                this.TipoDocumento = movement.MovementType.ToString();
                this.TotalDocumento = movement.DocumentTotals.GrossTotal;
                this.DataDocumento = movement.MovementDate;
                this.DataSistema = movement.SystemEntryDate;
                this.HashAnterior = hashDocumentoAnterior;
                this.HashActualDocumento = movement.Hash;
                string[] movementNo = movement.DocumentNumber.Split('/');
                if (movementNo != null && movementNo.Length == 2)
                {
                    int num = 0;
                    Int32.TryParse(movementNo[1], out num);
                    this.NumeroDocumento = num;

                    string[] tipoSerie = movementNo[0].Split(' ');
                    if (tipoSerie != null && tipoSerie.Length == 2)
                    {
                        this.SerieDocumento = tipoSerie[1];
                    }
                }

                this.ValoresLidosDocumento = true;
            }
        }

        public void InitFromMovement(SourceDocumentsWorkingDocumentsWorkDocument doc, string hashDocumentoAnterior)
        {
            if (doc != null)
            {
                this.TiposDocumentos = Enum.GetNames(typeof(Model.WorkType));
                this.TipoDocumento = doc.WorkType.ToString();
                this.TotalDocumento = doc.DocumentTotals.GrossTotal;
                this.DataDocumento = doc.WorkDate;
                this.DataSistema = doc.SystemEntryDate;
                this.HashAnterior = hashDocumentoAnterior;
                this.HashActualDocumento = doc.Hash;
                string[] movementNo = doc.DocumentNumber.Split('/');
                if (movementNo != null && movementNo.Length == 2)
                {
                    int num = 0;
                    Int32.TryParse(movementNo[1], out num);
                    this.NumeroDocumento = num;

                    string[] tipoSerie = movementNo[0].Split(' ');
                    if (tipoSerie != null && tipoSerie.Length == 2)
                    {
                        this.SerieDocumento = tipoSerie[1];
                    }
                }

                this.ValoresLidosDocumento = true;
            }
        }

        #region Binding properties
        bool valoresLidosDocumento;
        public bool ValoresLidosDocumento
        {
            get { return valoresLidosDocumento; }
            set
            {
                valoresLidosDocumento = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<CreateHashFromUserInputViewModel>(x => x.ValoresLidosDocumento));
            }
        }

        string mensagem;
        public string Mensagem
        {
            get { return mensagem; }
            set
            {
                mensagem = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<CreateHashFromUserInputViewModel>(x => x.Mensagem));
            }
        }

        string[] tiposDocumentos;
        public string[] TiposDocumentos
        {
            get { return tiposDocumentos; }
            set
            {
                tiposDocumentos = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<CreateHashFromUserInputViewModel>(x => x.TiposDocumentos));
            }
        }

        string tipoDocumento;
        public string TipoDocumento
        {
            get { return tipoDocumento; }
            set
            {
                tipoDocumento = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<CreateHashFromUserInputViewModel>(x => x.TipoDocumento));
            }
        }

        string serieDocumento;
        public string SerieDocumento
        {
            get { return serieDocumento; }
            set
            {
                serieDocumento = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<CreateHashFromUserInputViewModel>(x => x.SerieDocumento));
            }
        }

        int numeroDocumento;
        public int NumeroDocumento
        {
            get { return numeroDocumento; }
            set
            {
                numeroDocumento = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<CreateHashFromUserInputViewModel>(x => x.NumeroDocumento));
            }
        }

        decimal totalDocumento;
        public decimal TotalDocumento
        {
            get { return totalDocumento; }
            set
            {
                totalDocumento = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<CreateHashFromUserInputViewModel>(x => x.TotalDocumento));
            }
        }

        DateTime dataDocumento;
        public DateTime DataDocumento
        {
            get { return dataDocumento; }
            set
            {
                dataDocumento = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<CreateHashFromUserInputViewModel>(x => x.DataDocumento));
            }
        }

        DateTime dataSistema;
        public DateTime DataSistema
        {
            get { return dataSistema; }
            set
            {
                dataSistema = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<CreateHashFromUserInputViewModel>(x => x.DataSistema));
            }
        }

        string hashAnterior;
        public string HashAnterior
        {
            get { return hashAnterior; }
            set
            {
                hashAnterior = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<CreateHashFromUserInputViewModel>(x => x.HashAnterior));
            }
        }

        string hashDocumento;
        public string HashDocumento
        {
            get { return hashDocumento; }
            set
            {
                hashDocumento = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<CreateHashFromUserInputViewModel>(x => x.HashDocumento));
            }
        }

        string hashActualDocumento;
        public string HashActualDocumento
        {
            get { return hashActualDocumento; }
            set
            {
                hashActualDocumento = value;
                NotifyPropertyChanged(ObservableHelper.CreateArgs<CreateHashFromUserInputViewModel>(x => x.HashActualDocumento));
            }
        }
        #endregion

        #region Commands
        public SimpleCommand<object, object> DoCleanFieldsCommand { get; set; }
        private void CleanFields(object obj)
        {
            this.TipoDocumento = string.Empty;
            this.TotalDocumento = 0;
            this.DataDocumento = DateTime.Now;
            this.DataSistema = DateTime.Now;
            this.HashAnterior = string.Empty;
            this.HashActualDocumento = string.Empty;
            this.NumeroDocumento = 0;
            this.SerieDocumento = string.Empty;
            this.ValoresLidosDocumento = false;
        }
        private bool CanCleanFields(object arg)
        {
            return true;
        }

        public SimpleCommand<object, object> DoTestHashCommand { get; set; }
        private void TestHash(object obj)
        {
            this.Mensagem = "";

            object hasher = SHA1.Create();

            using (RSACryptoServiceProvider rsaCryptokey = new RSACryptoServiceProvider(1024))
            {
                StringBuilder toHash = new StringBuilder();

                try
                {
                    rsaCryptokey.FromXmlString(SAFTAnalyserLite.SAFT.SaftAnalyser.Instance.GetRSAPublicKey());
                }
                catch (Exception)
                {
                    this.Mensagem = "Houve um erro ao ler a chave pública.";
                    return;
                }

                byte[] stringToHashBuffer = Encoding.UTF8.GetBytes(FormatStringToHash());
                byte[] hashBuffer = Convert.FromBase64String(this.HashActualDocumento);

                if (rsaCryptokey.VerifyData(stringToHashBuffer, hasher, hashBuffer) == false)
                    this.Mensagem = "A assinatura do documento é inválida.";
            }
        }
        private bool CanTestHash(object arg)
        {
            return string.IsNullOrEmpty(this.TipoDocumento) == false && string.IsNullOrEmpty(this.SerieDocumento) == false && this.NumeroDocumento > 0 && File.Exists(SAFTAnalyserLite.SAFT.SaftAnalyser.Instance.PublicKeyFileName);
        }

        public SimpleCommand<object, object> DoCreateHashCommand { get; set; }
        private void CreateHash(object obj)
        {
            this.Mensagem = "";

            object hasher = SHA1.Create();

            using (RSACryptoServiceProvider rsaCryptokey = new RSACryptoServiceProvider(1024))
            {
                try
                {
                    rsaCryptokey.FromXmlString(SAFTAnalyserLite.SAFT.SaftAnalyser.Instance.GetRSAPrivateKey());
                }
                catch (Exception)
                {
                    this.Mensagem = "Houve um erro ao ler a chave privada.";
                    return;
                }

                byte[] stringToHashBuffer = Encoding.UTF8.GetBytes(FormatStringToHash());
                byte[] r = rsaCryptokey.SignData(stringToHashBuffer, hasher);

                this.HashDocumento = Convert.ToBase64String(r);

                if (string.IsNullOrEmpty(this.HashActualDocumento) == false && this.HashDocumento.IndexOf(this.HashActualDocumento, StringComparison.OrdinalIgnoreCase) < 0)
                    this.Mensagem = "A assinatura do documento é diferente da calculada.";
            }
        }
        private bool CanCreateHash(object arg)
        {
            return string.IsNullOrEmpty(this.TipoDocumento) == false && string.IsNullOrEmpty(this.SerieDocumento) == false && this.NumeroDocumento > 0 && File.Exists(SAFTAnalyserLite.SAFT.SaftAnalyser.Instance.PrivateKeyFileName);
        }

        string FormatStringToHash()
        {
            System.Globalization.CultureInfo en = new System.Globalization.CultureInfo("en-US");

            return string.Format("{0};{1};{2} {3}/{4};{5};{6}"
                        , this.DataDocumento.ToString("yyyy-MM-dd")
                        , this.DataSistema.ToString("yyyy-MM-ddTHH:mm:ss")
                        , this.TipoDocumento
                        , this.SerieDocumento
                        , this.NumeroDocumento
                        , this.TotalDocumento.ToString("0.00", en)
                        , this.HashAnterior);
        }
        #endregion

        PaneLocation preferredLocation = new PaneLocation { Location = PaneLocation.LEFT };
        public PaneLocation PreferredLocation
        {
            get { return preferredLocation; }
            set { preferredLocation = value; }
        }

        bool isAutoHidden = true;
        public bool IsAutoHidden
        {
            get { return isAutoHidden; }
            set { isAutoHidden = value; }
        }
    }
}
